package sstable

import (
	"encoding/binary"
	"encoding/json"
	"log"
	"os"
	"sort"
	"sync"
)

// SSTable表存储在磁盘中
type SSTable struct {
	f             *os.File // 文件句柄
	filePath      string
	tableMetaInfo MetaInfo            // 元数据
	sparseIndex   map[string]Position // 文件稀疏索引列表
	sortIndex     []string            // 排序后的 key 列表
	lock          sync.Locker         // 排他锁
}

func (table *SSTable) Init(path string) {
	table.filePath = path
	table.lock = &sync.Mutex{}
	table.loadFileHandle()
}

func (table *SSTable) loadFileHandle() {
	if table.f == nil {
		f, err := os.OpenFile(table.filePath, os.O_RDWR|os.O_CREATE, 0666)
		if err != nil {
			panic(err)
		}
		table.f = f
	}

	// 加载文件句柄的同时，加载表的元数据
	table.loadMetaInfo()
	table.loadSparseIndex()
}

// 从磁盘文件中读取 TableMetaInfo 元数据
/*
索引是数据区开始的
           dataLen             indexLen
｜-------------------------------------------------------｜
｜                          ｜            ｜              ｜
｜数据区                     ｜   索引区    ｜ 元数据        ｜
｜							｜            ｜              ｜
｜-------------------------------------------------------｜

indexStart|dataLen|dataStart|version
*/
// 从后往前读
func (table *SSTable) loadMetaInfo() {
	f := table.f
	_, err := f.Seek(0, 0)
	if err != nil {
		log.Println("error open file ", table.filePath)
	}
	info, _ := f.Stat()

	getMetaInfoItem := func(seekMove int64, itemValue *int64) {
		_, err := f.Seek(info.Size()-8*seekMove, 0)
		if err != nil {
			log.Println("error read metaInfo ", table.filePath)
			panic(err)
		}
		_ = binary.Read(f, binary.LittleEndian, itemValue)
	}
	getMetaInfoItem(5, &table.tableMetaInfo.version)
	getMetaInfoItem(4, &table.tableMetaInfo.dataStart)
	getMetaInfoItem(3, &table.tableMetaInfo.dataLen)
	getMetaInfoItem(2, &table.tableMetaInfo.indexStart)
	getMetaInfoItem(1, &table.tableMetaInfo.indexLen)
}

func (table *SSTable) loadSparseIndex() {
	// 加载稀疏索引区
	bytes := make([]byte, table.tableMetaInfo.indexLen)
	if _, err := table.f.Seek(table.tableMetaInfo.indexStart, 0); err != nil {
		log.Println(" error open file ", table.filePath)
		panic(err)
	}
	if _, err := table.f.Read(bytes); err != nil {
		log.Println(" error read sparse index ", table.filePath)
		panic(err)
	}

	// 反序列化，然后加载到内存中
	table.sparseIndex = make(map[string]Position)
	err := json.Unmarshal(bytes, &table.sparseIndex)
	if err != nil {
		log.Println(" error open file ", table.filePath)
		panic(err)
	}

	_, _ = table.f.Seek(0, 0)

	// 先排序
	keys := make([]string, 0, len(table.sparseIndex))
	for key := range table.sparseIndex {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	table.sortIndex = keys
}
